/**
 * This file is a part of qloudgen-rul. 
 * You can redistribute qloudgen-rul and/or modify it under the terms of the Lesser GNU General Public License version 3. 
 * qloudgen-rul is distributed WITHOUT ANY WARRANTY. 
 *
 * See the Lesser GNU General Public License for more details at http://www.gnu.org/licenses/. 
 **/
package org.qloudgen.rul.curator;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.recipes.cache.ChildData;
import org.apache.curator.framework.recipes.cache.PathChildrenCache;
import org.apache.curator.framework.recipes.cache.PathChildrenCacheEvent;
import org.apache.curator.framework.recipes.cache.PathChildrenCacheListener;
import org.apache.curator.utils.CloseableUtils;

import org.qloudgen.rul.drools.rule.DroolsRuleReceiver;
import org.qloudgen.sec.util.ExceptionUtil;

/**
 * <p>Title: Curator Framework Wrapper</p>
 * <p>Description: Wrapper repository cache receiver implementation.</p>
 * <p>Copyright: qloudgen/rul 2014</p>
 * @author <a href="mailto:hyq.dd.sun@gmail.com">glue.3M</a>
 */
public class RepositoryPathCacheReceiver extends AbstractPathCacheClient {
	/**
	 * Log output.
	 */
	private final static Logger logger = LoggerFactory.getLogger( RepositoryPathCacheReceiver.class );
	/**
	 *
	 */
	private PathChildrenCache cache = null;
	/**
	 *
	 */
	private DroolsRuleReceiver receiver = null;
	/**
	 *
	 */
	private long startTime = 0L;
	
	/**
	 * Default constructor.
	 */
	public RepositoryPathCacheReceiver() {
		//super();
	}
	
	/**
	 * Custom constructor.
	 *
	 * @param root
	 * @param configFile
	 */
	public RepositoryPathCacheReceiver(String root , String configFile) {
		super( root , configFile );
	}
	
	/**
	 * Sets current receiver.
	 *
	 * @param receiver
	 */
	public void setReceiver(final DroolsRuleReceiver receiver) {
		this.receiver = receiver;
	}
	
	/**
	 * Returns current receiver.
	 *
	 * @return DroolsRuleReceiver
	 */
	public DroolsRuleReceiver getReceiver() {
		return receiver;
	}
	
	/**
	 * Initailize.
	 */
	@Override
	public void init() {
		logger.info( "Starting initialize repository cache..." );
		//
		try {
			/**
			 * Parent initialize.
			 */
			super.init();
			/**
			 * Get current timestamp.
			 */
			startTime = System.currentTimeMillis();
			/**
			 * Create repository cache.
			 */
			cache = new PathChildrenCache( getClient() , getRoot() , true );
			/**
			 * Add repository cache listener.
			 */
			cache.getListenable().addListener( new PathChildrenCacheListener() {
				/**
				 *
				 */
				@Override
				public void childEvent(CuratorFramework client , PathChildrenCacheEvent event) throws Exception {
					//
					ChildData data = event.getData();
					//
					logger.trace( "Current child stat...{}" , data.getStat() );
					switch ( event.getType() ) {
						case CHILD_ADDED : {
							if ( data.getStat().getMtime() < startTime ) {
								logger.warn( "Current path added event expired and will be ignored...{}" , data.getPath() );
							}
							else {
								addChild( data.getPath() , data.getData() );
							}
							break;
						}
						case CHILD_UPDATED : {
							if ( data.getStat().getMtime() < startTime ) {
								logger.warn( "Current path updated event expired and will be ignored...{}" , data.getPath() );
							}
							else {
								updateChild( data.getPath() , data.getData() );
							}
							break;
						}
						case CHILD_REMOVED : {
							removeChild( data.getPath() );
							break;
						}
					}
				}
			} );
			/**
			 * Start repository cache.
			 */
			cache.start();
		}
		catch (Throwable t) {
			logger.error( "Current repository cache initialize error...\n{}" , ExceptionUtil.get( t ) );
			throw new CuratorException( t );
		}
		//
		logger.info( "Started initialize repository cache...{}" , this );
	}
	
	/**
	 * Destroy.
	 */
	@Override
	public void destroy() {
		logger.info( "Starting destroy repository cache..." );
		/**
		 * Destroy repository cache.
		 */
		CloseableUtils.closeQuietly( cache );
		cache = null;
		startTime = 0L;
		// Parent destroy.
		super.destroy();
		logger.info( "Started destroy repository cache..." );
	}
	
	/**
	 * Added child.
	 *
	 * @param path
	 * @param data
	 */
	public void addChild(String path , byte[] data) {
		doNotify( path , data );
	}
	
	/**
	 * Updated child.
	 *
	 * @param path
	 * @param data
	 */
	public void updateChild(String path , byte[] data) {
		doNotify( path , data );
	}
	
	/**
	 * Removed child.
	 *
	 * @param path
	 */
	public void removeChild(String path) {
		doNotify( path , null );
	}
	
	/**
	 * Notify.
	 *
	 * @param path
	 * @param data
	 */
	private void doNotify(String path , byte[] data) {
		logger.trace( "Current node path is...{}" , path );
		logger.trace( "Current node data is...{}" , data == null ? "" : new String( data ) );
		/**
		 * Send to drools scanner.
		 */
		try {
			String[] splits = getReleaseIdFromPath( path ).split( ":" );
			receiver.receive( splits[0] , splits[1] , splits[2] );
		}
		catch (Throwable t) {
			logger.error( "Current repository cache notify error...\n{}" , ExceptionUtil.get( t ) );
			throw new CuratorException( t );
		}
	}
	
	/**
	 * Returns the String representation.
	 *
	 * @return String
	 */
	@Override
	public String toString() {
		StringBuilder builder = new StringBuilder();
		builder.append( "[" );
		builder.append( super.toString() );
		builder.append( "-->" );
		builder.append( "cache=" ).append( cache ).append( ";" );
		builder.append( "receiver=" ).append( receiver );
		builder.append( "]" );
		//
		return builder.toString();
	}
}